home *** CD-ROM | disk | FTP | other *** search
/ Just Call Me Internet / Just Call Me Internet.iso / prog / atari / c / snz128s / src / snews1.c < prev    next >
C/C++ Source or Header  |  1994-06-02  |  40KB  |  1,412 lines

  1. /*
  2.     SNEWS 2.0
  3.  
  4.     snews - a simple threaded news reader
  5.  
  6.  
  7.     Copyright (C) 1991    John McCombs, Christchurch, NEW ZEALAND
  8.                         john@ahuriri.gen.nz
  9.                         PO Box 2708, Christchurch, NEW ZEALAND
  10.  
  11.     This program is free software; you can redistribute it and/or modify
  12.     it under the terms of the GNU General Public License, version 1, as
  13.     published by the Free Software Foundation.
  14.  
  15.     This program is distributed in the hope that it will be useful,
  16.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.     GNU General Public License for more details.
  19.  
  20.     See the file COPYING, which contains a copy of the GNU General
  21.     Public License.
  22.  
  23.     Atari version ported by Graham Judd - gjudd@siward.demon.co.uk
  24.  
  25.  */
  26.  
  27. /*---------------------------- Source Control ------------------------------*/
  28.  
  29. /*
  30.  * $Id: snews1.c,v 1.3 1994/02/05 19:34:56 gbj Exp user $
  31.  */
  32.  
  33.  
  34. /****************************************************************************
  35. *   20 May 92   1.2     GT  ka9q mods.                                      *
  36. *   22 May 92   1.3     GT  Set time zone.                                  *
  37. *   05 Jun 92   1.4     GT  Nikki Locke's SPACE command.                    *
  38. *   06 Jun 92   1.5     GT  Invalidate freed pointers.                      *
  39. *   09 Jun 92   1.6     GT  Right and left cursor keys.                     *
  40. *   12 Jun 92   1.7     NJL Add v_init call & change to use scr_lines.      *
  41. *                           Add 'q' as alternative to ESC throughout.       *
  42. *                           Fix memory leak when Escaping from read_thread. *
  43. *                           Ensure strtok() accepts tab as well as space.   *
  44. *   17 Jul 92   1.8     GT  C++ compilation.                                *
  45. *                           Heap debugging.                                 *
  46. *                           Swap out when executing child process.          *
  47. *   16 Aug 92   1.9    MSM  Snews 1.9                                       *
  48. *                           Lock history etc. during use                    *
  49. *   31 May 93   1.10   MSM  Snews 2.0                                       *
  50. *   18 Jun 93   1.11   MSM  Heap debugging removed (using Bounds Check)     *
  51. *   10 Jul 93   1.12   MSM  New commands added, help updated                *
  52. *    7 Aug 93   1.13   MSM  Change command functionality to be TINish       *
  53. *                      MSM  Add expert mode                                 *
  54. *                      MSM  Add confirm exit mode                           *
  55. *   26 Aug 93   1.13   MSM  Colour Support clean up, Expert toggle, bugs    *
  56. *    3 Oct 92   1.14   MSM  Case sensitive y/n questions made insensitive   *
  57. *                           old style tab action added, new config options  *
  58. *                           132 col / 43 line support corrected             *
  59. *                           article/thread save function corrected.         *
  60. *   24 Nov 93   1.15   MSM  Changed to Delta version control                *
  61. *                           Session setting text changes                    *
  62. *   25 Nov 93   1.16   MSM  Correct read list processing                    *
  63. *    5 Feb 94    AT2    GBJ  Ported to Atari ST                              *
  64. *    2 Apr 94   1.17   MSM  Add print thread support                        *
  65. *                abort/replace/append choice for thread save     *
  66. *                15.89 style added to smart match                *
  67. *                Save full newsgroup                    *
  68. *                Suspend support                    *
  69. *****************************************************************************/
  70.  
  71.  
  72. #include <time.h>
  73. #include <io.h>
  74. #include <fcntl.h>
  75. #include "defs.h"
  76. #include "snews.h"
  77. #include "screen.h"
  78. #ifdef ATARI
  79. #    include <sys/types.h>
  80. #    include "st.h"
  81. #    include "fileops.h"
  82. #else
  83. #    include "exec.h"
  84. #endif
  85. #include "locking.h"
  86.  
  87. #ifndef __TURBOC__
  88. #ifndef ATARI
  89. #include <graph.h>
  90. #include <dos.h>
  91. #define BLACK 0
  92. #define LIGHTGRAY 7
  93. unsigned long farcoreleft(void);
  94. #endif
  95. #endif
  96.  
  97.  
  98. int             xfile = FALSE;
  99.  
  100. INFO            my_stuff;
  101. #ifdef ATARI
  102. unsigned long _STACK = 32768;
  103. #else
  104. unsigned        _stklen = 16384;
  105. #endif
  106. char     search_text[128];
  107. char            save_name[65];
  108. char     search_group[80];
  109.  
  110. /*------------------------------- main --------------------------------*/
  111. void            main(void)
  112. {
  113.     ACTIVE         *gp, *head;
  114.     ACTIVE         *tmp_ng;
  115.     int             articles, j, unread;
  116.     int             done;
  117.  
  118.     tzset();
  119.     fprintf(stderr, "loading config... ");
  120.     if (load_stuff()) {
  121.  
  122.         v_init(my_stuff.directvideo);      /* initialise bios video package */
  123.         if (mlock(my_stuff.news_dir, "history", "Snews") != 0) {
  124.             printf("snews: Unable to lock history!\n");
  125.             exit(2);
  126.         }
  127.         if (mlock(my_stuff.news_dir, my_stuff.user, "Snews") != 0) {
  128.             printf("snews: Unable to lock user %s!\n", my_stuff.user);
  129.             rmlock(my_stuff.news_dir, "history", "Snews");
  130.             exit(2);
  131.         }
  132.         textbackground(textb);
  133.         textcolor(textf);
  134.         gotoxy(1, 1);
  135.         clrscr();
  136.         gotoxy((scr_cols/2) - 23, (scr_rows/2) - 8);
  137.         printf("D E M O N   I N T E R N E T   S E R V I C E S");
  138.         gotoxy((scr_cols/2) - 18, (scr_rows/2) - 6);
  139.         printf("U s e n e t   N e w s   R e a d e r");
  140.         gotoxy((scr_cols/2) - 11, (scr_rows/2) - 4);
  141.         printf("Version %d.%02d [build %d]", rmj, rmm, rup);
  142.         gotoxy((scr_cols/2) - 20, (scr_rows/2) - 2);
  143.         printf("Based on Simple News 2.0 by John McCombs");
  144. #ifdef ATARI
  145.         gotoxy((scr_cols/2) - 17, (scr_rows/2));
  146.         textbackground(headb);
  147.         textcolor(headf);
  148.         printf("Support: gjudd@siward.demon.co.uk");
  149.         textbackground(textb);
  150.         textcolor(textf);
  151. #else
  152.         gotoxy((scr_cols/2) - 14, (scr_rows/2));
  153.         printf("Support: internet@demon.net");
  154. #endif
  155.         gotoxy((scr_cols/2) - 1, (scr_rows/2) + 2);
  156.         printf("or");
  157.         gotoxy((scr_cols/2) - 26, (scr_rows/2) + 4);
  158.         printf("Type 'B' at any menu and complete the questionnaire");
  159.         gotoxy((scr_cols/2) - 31, (scr_rows/2) + 6);
  160.         printf("Source available under GNU Licence by FTP from ftp.demon.co.uk");
  161.  
  162.         gotoxy(1, scr_rows - 1);
  163.         printf("loading active... ");
  164.         head = load_active_file();
  165.         add_suspend(head);
  166.         printf("loading read list... ");
  167.         load_read_list();
  168.         printf("loading history... ");
  169.         load_history_list(1);
  170.  
  171.         done = FALSE;
  172.         gp = NULL;
  173.         strcpy(search_group, "demon.announce");
  174.  
  175.         while (!done) {
  176.             if ((gp = select_group(head, gp)) != NULL) {
  177.                 if ((read_group(gp) == EX_NEXT) && (gp->next != NULL)) {
  178.                     tmp_ng = gp->next;
  179.                     unread = 0;
  180.                     while (tmp_ng != NULL) {
  181.                         articles = (int) (tmp_ng->hi_num - tmp_ng->lo_num);
  182.                         for (j=0;j<articles;j++) {
  183.                             if (*((tmp_ng->read_list) + j) == FALSE)
  184.                                 unread++;
  185.                         }
  186.                         if (unread > 0)
  187.                             break;
  188.                         tmp_ng= tmp_ng->next;
  189.                     }
  190.                     if (unread > 0)
  191.                         gp = tmp_ng;
  192.                     else
  193.                         message("-- No more articles to read --");
  194.                 }
  195.             }
  196.             else
  197.                 done = TRUE;
  198.         }
  199.  
  200.         textbackground(textb);
  201.         textcolor(textf);
  202.         clrscr();
  203.  
  204.         free_hist_list();
  205.         save_read_list();
  206.         close_active_file();
  207.         rmlock(my_stuff.news_dir, "history", "Snews");
  208.         rmlock(my_stuff.news_dir, my_stuff.user, "Snews");
  209.     }
  210.     else {
  211.         fprintf(stderr, "Couldn't find neccessary item in the .rc files\n");
  212.     }
  213. #ifdef ATARI
  214.     printf("\033q\n");
  215. #endif
  216. }
  217.  
  218. /*-------------------------- find which group to read ----------------------*/
  219. ACTIVE         *select_group(ACTIVE * head, ACTIVE * current)
  220. {
  221.  
  222.     /*
  223.      * Present the list of groups, and allow him to move up and down with
  224.      * the arrow and PgUp and PgDn keys.  'h' for help
  225.      */
  226.  
  227.     ACTIVE         *top;                  /* newsgroup at the top of the page */
  228.     ACTIVE         *this_group;              /* current newsgroup */
  229.     ACTIVE         *tmp_ng;
  230.     int             exit_code;              /* why we are exiting the loop */
  231.     char            sub_tmp[80];
  232.  
  233.     int             ch, i, j, articles, unread, ch2;
  234.  
  235.     this_group = (current == NULL) ? head : current;
  236.  
  237.     top = head;
  238.     exit_code = 0;
  239.  
  240.     show_groups(&top, this_group, TRUE, head);
  241.  
  242.     while (exit_code == 0) {
  243.  
  244.         ch = getch();
  245.         switch (ch) {
  246.  
  247.           case 0:
  248.           case 0xE0:
  249.             {
  250.                 ch = getch();
  251.                 switch (ch) {
  252.  
  253.                   case Fn1:
  254.                     show_help(HELP_GROUP);
  255.                     show_groups(&top, this_group, TRUE, head);
  256.                     break;
  257.  
  258.                   case Fn2:
  259.                     show_values();
  260.                     show_groups(&top, this_group, TRUE, head);
  261.                     break;
  262.  
  263.                   case Fn3:
  264.                     change_values();
  265.                     show_groups(&top, this_group, TRUE, head);
  266.                     break;
  267.  
  268.                   case UP_ARR:
  269.                         if (this_group->last != NULL)
  270.                             this_group = this_group->last;
  271.                         break;
  272.  
  273.                   case DN_ARR:
  274.                         if (this_group->next != NULL)
  275.                             this_group = this_group->next;
  276.                         break;
  277.  
  278.                   case PGUP:
  279.                         for (i = this_group->index - top->index;
  280.                                               i < PAGE_LENGTH; i++) {
  281.                             if (this_group->last == NULL)
  282.                                 break;
  283.                             this_group = this_group->last;
  284.                         }
  285.                         break;
  286.  
  287.                   case PGDN:
  288.                         for (i = this_group->index - top->index;
  289.                                               i < PAGE_LENGTH; i++) {
  290.                             if (this_group->next == NULL)
  291.                                 break;
  292.                             this_group = this_group->next;
  293.                         }
  294.                         break;
  295.  
  296.                   case HOME:
  297.                         top = this_group = head;
  298.                         show_groups(&top, this_group, TRUE, head);
  299.                         break;
  300.  
  301.                   case END:
  302.                         top = this_group = head;
  303.                         while (this_group->next != NULL)
  304.                             this_group = this_group->next;
  305.                         break;
  306.  
  307.                   case RT_ARR:
  308.                         exit_code = EX_DONE;
  309.                         break;
  310.  
  311.                 }                          /* switch (ch) */
  312.  
  313.                 break;
  314.             }
  315.  
  316.           case '!':
  317.             textbackground(headb);
  318.             textcolor(headf);
  319.             clrscr();
  320. #ifdef ATARI
  321.             printf("Sorry, function not supported. \n\n");
  322.             printf("Press a key to return to Snews.\n\n");
  323.             getch();
  324. #else
  325.             printf("Type <EXIT> to return to Snews.\n\n");
  326.             system("");
  327. #endif
  328.             textbackground(textb);
  329.             textcolor(textf);
  330.             clrscr();
  331.             show_groups(&top, this_group, TRUE, head);
  332.             break;
  333.  
  334.           case 'k':
  335.             if (this_group->last != NULL)
  336.                 this_group = this_group->last;
  337.             break;
  338.  
  339.           case 'j':
  340.             if (this_group->next != NULL)
  341.                 this_group = this_group->next;
  342.             break;
  343.  
  344.           case    2:    /* ^B */
  345.           case   21:    /* ^U */
  346.           case  'b':
  347.             for (i = 0; i < PAGE_LENGTH; i++) {
  348.                 if (this_group->last == NULL)
  349.                     break;
  350.                 this_group = this_group->last;
  351.             }
  352.             break;
  353.  
  354.           case   4: /* ^D */
  355.           case   6: /* ^F */
  356.           case ' ':
  357.             for (i = 0; i < PAGE_LENGTH; i++) {
  358.                 if (this_group->next == NULL)
  359.                     break;
  360.                 this_group = this_group->next;
  361.             }
  362.             break;
  363.  
  364.           case 'w':
  365.             strcpy(sub_tmp, "");
  366.             if (this_group->suspend == TRUE) {
  367.                 message("This group is suspende, post regardless ? (y/n ");
  368.                 ch2 = getch();
  369.                 ch2 = tolower(ch2);
  370.                 message("");
  371.             }
  372.             else
  373.                 ch2 = 'y';
  374.             if (ch2 == 'y') {
  375.                 post(NULL, this_group->group, sub_tmp);
  376.                 show_groups(&top, this_group, TRUE, head);
  377.             }
  378.             break;
  379.  
  380.           case 'v':
  381. #ifdef ATARI
  382.             sprintf(sub_tmp, "Demon Internet Simple News v%02d.%02d.%02d LC5 compiled %s", rmj, rmm, rup, __DATE__);
  383. #else
  384. #ifdef __TURBOC__
  385.             sprintf(sub_tmp, "Demon Internet Simple News v%02d.%02d.%02d BC++ compiled %s", rmj, rmm, rup, __DATE__);
  386. #else
  387.             sprintf(sub_tmp, "Demon Internet Simple News v%02d.%02d.%02d MSVC compiled %s", rmj, rmm, rup, __DATE__);
  388. #endif
  389. #endif
  390.             message(sub_tmp);
  391.             getch();
  392.             show_groups(&top, this_group, TRUE, head);
  393.             break;
  394.  
  395.           case 's':
  396.           case 'S':
  397.               save_group_to_disk(this_group);
  398.               message("");
  399.               break;
  400.             
  401.           case 'H':
  402.           case 'h':
  403.             show_help(HELP_GROUP);
  404.             show_groups(&top, this_group, TRUE, head);
  405.             break;
  406.  
  407.           case 'Z':
  408.           case 'z':
  409.             mark_group_as_unread(this_group);
  410.             show_groups(&top, this_group, TRUE, head);
  411.             break;
  412.  
  413.           case 'g':
  414.             this_group = search_for_group(head, TRUE);
  415.             break;
  416.  
  417.           case '/':
  418.           case '+':
  419.           case '=':
  420.             this_group = search_for_group(this_group, TRUE);
  421.             break;
  422.  
  423.           case '?':
  424.           case '-':
  425.             this_group = search_for_group(this_group, FALSE);
  426.             break;
  427.  
  428.           case 'M':
  429.             mail_to_someone(NULL);
  430.             show_groups(&top, this_group, TRUE, head);
  431.             break;
  432.  
  433.           case 'c':
  434.             if (mark_group_as_read(this_group, TRUE) == TRUE)
  435.                 show_groups(&top, this_group, TRUE, head);
  436.             else
  437.                 message("");
  438.             break;
  439.  
  440.           case 'C':
  441.             if (mark_group_as_read(this_group, TRUE) != TRUE) {
  442.                 message("");
  443.                 break;
  444.             }
  445.             show_groups(&top, this_group, TRUE, head);
  446.             /* and drop into 'N' code */
  447.  
  448.           case 'N':
  449.             tmp_ng = this_group;
  450.             unread = 0;
  451.             articles = (int) (tmp_ng->hi_num - tmp_ng->lo_num);
  452.             for (j=0; j < articles; j++) {
  453.                 if (*((tmp_ng->read_list) + j) == FALSE)
  454.                     unread++;
  455.             }
  456.             if (unread > 0)    {
  457.                 break;
  458.             }
  459.             else
  460.                 tmp_ng = this_group->next;
  461.             unread = 0;
  462.             while (tmp_ng != NULL) {
  463.                 articles = (int) (tmp_ng->hi_num - tmp_ng->lo_num);
  464.                 for (j = 0; j < articles; j++) {
  465.                     if (*((tmp_ng->read_list) + j) == FALSE)
  466.                         unread++;
  467.                 }
  468.                 if (unread > 0) {
  469.                      break;
  470.                 }
  471.                 tmp_ng = tmp_ng->next;
  472.             }
  473.             if (unread > 0) {
  474.                 this_group = tmp_ng;
  475.             }
  476.             else {
  477.                 message("-- No more unread articles --");
  478.             }
  479.             break;
  480.  
  481.           case TAB:
  482.           case 'n':
  483.             if (my_stuff.tab_action == TRUE) {
  484.                 tmp_ng = this_group;
  485.                 unread = 0;
  486.                 articles = (int) (tmp_ng->hi_num - tmp_ng->lo_num);
  487.                 for (j=0; j < articles; j++) {
  488.                     if (*((tmp_ng->read_list) + j) == FALSE)
  489.                         unread++;
  490.                 }
  491.                 if ((unread > 0) && (tmp_ng->suspend == FALSE))    {
  492.                     exit_code = EX_DONE;
  493.                     break;
  494.                 }
  495.                 else
  496.                     tmp_ng = this_group->next;
  497.                 unread = 0;
  498.                 while (tmp_ng != NULL) {
  499.                     articles = (int) (tmp_ng->hi_num - tmp_ng->lo_num);
  500.                     for (j = 0; j < articles; j++) {
  501.                         if (*((tmp_ng->read_list) + j) == FALSE)
  502.                             unread++;
  503.                     }
  504.                     if ((unread > 0) && (tmp_ng->suspend == FALSE)) {
  505.                            exit_code = EX_DONE;
  506.                         break;
  507.                     }
  508.                     unread = 0;
  509.                     tmp_ng = tmp_ng->next;
  510.                 }
  511.                 if ((unread > 0) && (tmp_ng->suspend == FALSE)) {
  512.                     this_group = tmp_ng;
  513.                 }
  514.                 else {
  515.                     message("-- No more articles to read --");
  516.                 }
  517.                 break;
  518.             }
  519.             else { /* old style action */
  520.                 tmp_ng = this_group->next;
  521.                 unread = 0;
  522.                 while (tmp_ng != NULL) {
  523.                     articles = (int) (tmp_ng->hi_num - tmp_ng->lo_num);
  524.                     for (j = 0; j < articles; j++) {
  525.                         if ( *((tmp_ng->read_list)+j) == FALSE)
  526.                             unread++;
  527.                     }
  528.                     if ((unread > 0) && (tmp_ng->suspend == FALSE))
  529.                          break;
  530.                     unread = 0;
  531.                     tmp_ng = tmp_ng->next;
  532.                 }
  533.                 if (unread > 0) {
  534.                     this_group = tmp_ng;
  535.                 } else {
  536.                     message("-- No more articles to read --");
  537.                 }
  538.                 break;
  539.             }
  540.  
  541.           case ENTER:
  542.             exit_code = EX_DONE;
  543.             break;
  544.  
  545.           case ESCAPE:
  546.           case 'Q':
  547.           case 'q':
  548.             if (my_stuff.exitconfirm != 1) {
  549.                 message(" EXIT - Are you sure (y/n) ? ");
  550.                 ch = getch();
  551.                 if (ch == 'y')
  552.                     exit_code = EX_QUIT;
  553.                 else
  554.                     show_groups(&top, this_group, TRUE, head);
  555.             }
  556.             else {
  557.                 exit_code = EX_QUIT;
  558.             }
  559.             break;
  560.  
  561.           case 'B':
  562.             bug_report();
  563.             show_groups(&top, this_group, TRUE, head);
  564.             break;
  565.  
  566.         };
  567.         if (exit_code == 0)
  568.             show_groups(&top, this_group, FALSE, head);
  569.     }
  570.  
  571.     if (exit_code == EX_DONE)
  572.         return (this_group);
  573.     else
  574.         return (NULL);
  575.  
  576. }
  577.  
  578. /*----------------------- Search for a newsgroup --------------------------*/
  579. ACTIVE           *search_for_group(ACTIVE *this_group, int direction)
  580. {
  581.     char              pattern[128];
  582.     char              prompt[80];
  583.     ACTIVE           *act;
  584.     int               found=FALSE;
  585. #ifdef ATARI
  586.     ACTIVE         *hit;
  587.     /*
  588.        This has been added because, as the routine stands, if the
  589.        matched group is the very last in the list, then act == NULL
  590.        and the assignment of act->last, act->next depending upon
  591.        direction causes a bus error.  So, hit is set to act which will
  592.        not be NULL UNLESS the search string does not appear in ANY
  593.        group name.  If hit is NULL, this_group is returned.  
  594.     */
  595. #endif
  596.  
  597.     sprintf(prompt, "Search %s for [%s] ? ", direction ? "forwards" : "backwards",
  598.         search_group);
  599.     lmessage(prompt);
  600.     gets(pattern);
  601.     if (pattern[0] == 0x1b) {
  602.         lmessage("");
  603.         return this_group;
  604.     }
  605.  
  606.     if (strlen(pattern) > 0) {
  607.         strcpy(search_group, pattern);
  608.     }
  609.     else {
  610.         strcpy(pattern, search_group);
  611.     }
  612.  
  613.     message ("*** searching - please wait ***");
  614.     hit = NULL;
  615.     strlwr(pattern);
  616.     if (direction) {
  617.         for (act=this_group->next;!found && act!=NULL;act=act->next) {
  618.             found = (strstr(act->group, pattern) != NULL);
  619. #ifdef ATARI
  620.             if (found) {
  621.                 hit = act;
  622.                 break;
  623.             }
  624. #endif
  625.         }
  626.     }
  627.     else {
  628.         for(act=this_group->last;!found && act!=NULL;act=act->last) {
  629.             found = (strstr(act->group, pattern) != NULL);
  630. #ifdef ATARI
  631.             if (found) {
  632.                 hit = act;
  633.                 break;
  634.             }
  635. #endif
  636.         }
  637.     }
  638.  
  639.     lmessage("");
  640.  
  641. #ifdef ATARI
  642.     if (found && hit) {
  643. #else
  644.     if (found) {
  645. #endif
  646.         lmessage("");
  647. #ifdef ATARI
  648.         return hit;
  649. #else
  650.         return(direction ? act->last : act->next);
  651. #endif
  652.     }
  653.     else {
  654.         sprintf(prompt, "*** %s not found - press any key ***", pattern);
  655.         message(prompt);
  656.         getch();
  657.         message("");
  658.         return this_group;
  659.     }
  660. }
  661.  
  662. /*---------------------------- help screen ---------------------------------*/
  663. void            show_help(int h)
  664. {
  665.  
  666.     char           *type[] = {"Newsgroup", "Thread", "Article"};
  667.     char            buf[256];
  668.  
  669.     textbackground(helpb);
  670.     textcolor(helpf);
  671.     clrscr();
  672.     textbackground(headb);
  673.     textcolor(headf);
  674.     clreol();
  675.     sprintf(buf, "%s Help    (%s)\r\n", type[h], VERSION);
  676.     gotoxy((scr_cols/2) - (strlen(buf)/2), 1);
  677.     cprintf(buf);
  678.     clreol();
  679.     textbackground(helpb);
  680.     textcolor(helpf);
  681.  
  682.     switch (h) {
  683.       case HELP_GROUP:
  684.  
  685.         cprintf("\r\n\r\n");
  686.         cprintf("   LIST NEWSGROUPS                     CHOOSING NEWSGROUPS\r\n\r\n");
  687.         cprintf("   sUP   move display up one page      TAB    go to next unread newsgroup\r\n");
  688.         cprintf("   sDN   move display down one page    ENTER  read selected newsgroup\r\n");
  689.         cprintf("   Home  move to top of list           RIGHT  read selected newsgroup\r\n");
  690.         cprintf("   End   move to bottom of list\r\n");
  691.         cprintf("   UP    move up one line\r\n");
  692.         cprintf("   DOWN  move down one line\r\n\r\n");
  693.         cprintf("   POST ARTICLE                        SPECIAL ACTIONS\r\n\r\n");
  694.         cprintf("   w     post article in newsgroup     c      mark this newsgroup as read\r\n");
  695.         cprintf("                                       C      mark group as read and goto next\r\n");
  696.         cprintf("                                       z      mark this newsgroup as unread\r\n");
  697.         cprintf("                                       F2     show user values\r\n");
  698.         cprintf("                                       F3     Toggle Expert Mode\r\n");
  699.         cprintf("                                       ESCAPE exit Snews\r\n");
  700.         cprintf("                                       q      exit Snews\r\n");
  701.         cprintf("                                       v      show version information\r\n");
  702.  
  703.         message("-- Press any key for next page --");
  704.         getch();
  705.  
  706.         textbackground(helpb);
  707.         textcolor(helpf);
  708.         clrscr();
  709.         textbackground(headb);
  710.         textcolor(headf);
  711.         clreol();
  712.         sprintf(buf, "%s Help    (%s)\r\n", type[h], VERSION);
  713.         gotoxy((scr_cols/2) - (strlen(buf)/2), 1);
  714.         cprintf(buf);
  715.         clreol();
  716.         textbackground(helpb);
  717.         textcolor(helpf);
  718.  
  719.         cprintf("\r\n\r\n");
  720.         cprintf("   MAIL\r\n\r\n");
  721.         cprintf("   M     mail to someone\r\n\r\n");
  722.  
  723.         cprintf("   SEARCHING\r\n\r\n");
  724.  
  725.         cprintf("   g     search for newsgroup by name\r\n");
  726.         cprintf("  + /    search following for newsgroup\r\n");
  727.         cprintf("  - ?    search preceeding for newsgroups\r\n\r\n");
  728.  
  729.         cprintf("   MISCELLANEOUS\r\n\r\n");
  730.         cprintf("   !     shell out (NOT SUPPORTED)\r\n");
  731.         cprintf("   s     save all articles in newsgroup to disk\r\n");
  732.         cprintf("   B     mail a support query to DEMON\r\n");
  733.  
  734.         break;
  735.  
  736.       case HELP_THREAD:
  737.         cprintf("\r\n\r\n");
  738.         cprintf("   LISTING THREADS                     READING THREADS\r\n\r\n");
  739.  
  740.         cprintf("   sUP   move display up one page      TAB    read next unread article\r\n");
  741.         cprintf("   sDN   move display down one page    BACKSP read the last/prev. article\r\n");
  742.         cprintf("   Home  move to top of list           ENTER  read the selected thread\r\n");
  743.         cprintf("   End   move to bottom of list        RIGHT  read the selected thread\r\n");
  744.         cprintf("   UP    move up one line              LEFT   display newsgroups\r\n");
  745.         cprintf("   DOWN  move down one line            q      display newsgroups\r\n\r\n");
  746.  
  747.         cprintf("   MARKING THREADS                    EXTRACT THREADS\r\n\r\n");
  748.  
  749.         cprintf("   c     mark as read, go to groups    s      save thread to disk\r\n");
  750.         cprintf("   C     mark newsgroup as read        S      extract thread to mail \"%s\"\r\n", my_stuff.extruser);
  751.         cprintf("   K     mark current thread as read          in mail directory\r\n");
  752.         cprintf("   X     mark all threads before\r\n");
  753.         cprintf("         current as read\r\n");
  754.         cprintf("   z     mark current thread as unread\r\n");
  755.  
  756.         message("-- Press any key for next page --");
  757.         getch();
  758.  
  759.         textbackground(helpb);
  760.         textcolor(helpf);
  761.         clrscr();
  762.         textbackground(headb);
  763.         textcolor(headf);
  764.         clreol();
  765.         sprintf(buf, "%s Help    (%s)\r\n", type[h], VERSION);
  766.         gotoxy((scr_cols/2) - (strlen(buf)/2), 1);
  767.         cprintf(buf);
  768.         clreol();
  769.         textbackground(helpb);
  770.         textcolor(helpf);
  771.  
  772.         cprintf("\r\n\r\n");
  773.         cprintf("   MAIL                               POSTING\r\n\r\n");
  774.  
  775.         cprintf("   M     mail to someone              w       post article to newsgroup\r\n\r\n");
  776.         
  777.         cprintf("   SEARCHING\r\n\r\n");
  778.         cprintf("   +     search for text in following subjects\r\n");
  779.         cprintf("   /     search for text in following bodies\r\n");
  780.         cprintf("   -     search for text in preceeding subjects\r\n");
  781.         cprintf("   ?     search for text in preceeding bodies\r\n\r\n");
  782.         
  783.         cprintf("   MISCELLANEOUS\r\n\r\n");
  784.  
  785.         cprintf("   F2    show user values             v       show version information\r\n");
  786.         cprintf("   F3    Toggle Expert Mode\r\n");
  787.         cprintf("   !     shell out (NOT SUPPORTED)\r\n");
  788.         cprintf("   B     mail a support query to DEMON\r\n");
  789.  
  790.         break;
  791.  
  792.       case HELP_ARTICLES:
  793.  
  794.         cprintf("\r\n\r\n");
  795.         cprintf("   READING ARTICLES                    CHOOSING ARTICLES\r\n\r\n");
  796.  
  797.         cprintf("   sUP   move display up one page      TAB    read next page / unread article\r\n");
  798.         cprintf("   sDN   move display down one page    ENTER  read next article\r\n");
  799.         cprintf("   Home  move to top of article        RIGHT  read next article in thread\r\n");
  800.         cprintf("   End   move to bottom of article     LEFT   read prior article in thread\r\n");
  801.         cprintf("   UP    move up one line              p      read prior article in thread\r\n");
  802.         cprintf("   DOWN  move down one line            <      read first article in thread\r\n");
  803.         cprintf("   b     move back one page            >      read last article in thread\r\n");
  804.         cprintf("   n     mark this article as read\r\n");
  805.         cprintf("         and read next unread          MISCELLANEOUS\r\n");
  806.         cprintf("   K     mark this and remainder of\r\n");
  807.         cprintf("         thread as read                !      shell out (NOT SUPPORTED)\r\n");
  808.         cprintf("   z     mark this article unread\r\n");
  809.         cprintf("   d     ROT13 this article\r\n");
  810.  
  811.         message("-- Press any key for next page --");
  812.         getch();
  813.  
  814.         textbackground(helpb);
  815.         textcolor(helpf);
  816.         clrscr();
  817.         textbackground(headb);
  818.         textcolor(headf);
  819.         clreol();
  820.         sprintf(buf, "%s Help    (%s)\r\n", type[h], VERSION);
  821.         gotoxy((scr_cols/2) - (strlen(buf)/2), 1);
  822.         cprintf(buf);
  823.         clreol();
  824.         textbackground(helpb);
  825.         textcolor(helpf);
  826.  
  827.         cprintf("\r\n\r\n");
  828.         cprintf("   POST ARTICLE                        SEND MAIL\r\n\r\n");
  829.  
  830.         cprintf("   w     post new article              r    mail reply to author\r\n");
  831.         cprintf("   f     post follow-up article        R    mail reply to someone\r\n");
  832.         cprintf("                                       m    mail article to someone\r\n");
  833.         cprintf("                                       M    mail to someone\r\n\r\n");
  834.  
  835.         cprintf("   SPECIAL ACTION                      EXTRACT ARTICLES\r\n\r\n");
  836.  
  837.         cprintf("   d     decode ROT-13 article         s    save article to file\r\n");
  838.         cprintf("   F2    show user values              S    extract article to mail \"%s\"\r\n", my_stuff.extruser);
  839.         cprintf("   F3    Toggle Expert Mode                 in mail directory\r\n");
  840.         cprintf("   B     Mail a support query          o    print article\n\r");
  841.         cprintf("   v     show version information\r\n\r\n");
  842.  
  843.         cprintf("   SEARCHING\r\n\r\n");
  844.  
  845.         cprintf("  + /    search for text in following articles\r\n");
  846.         cprintf("  - ?    search for text in preceding articles\r\n");
  847.  
  848.         break;
  849.     };
  850.  
  851.     message("-- Press any key to continue --");
  852.     getch();
  853.  
  854. }
  855.  
  856.  
  857. /*-------------- show the values of user defined variables -------------*/
  858. void            show_values(void)
  859. {
  860.     char            buf[256];
  861.  
  862.     textbackground(helpb);
  863.     textcolor(helpf);
  864.     clrscr();
  865.     textbackground(headb);
  866.     textcolor(headf);
  867. #ifdef ATARI
  868.     printf("%*s", scr_cols, " ");
  869. #else
  870.     clreol();
  871. #endif
  872.     sprintf(buf, "Pre-set User values  (%s)\r\n", VERSION);
  873.     gotoxy((scr_cols/2) - (strlen(buf)/2), 1);
  874.     cprintf(buf);
  875. #ifndef ATARI
  876.     clreol();
  877. #endif
  878.     textbackground(helpb);
  879.     textcolor(helpf);
  880. #ifndef ATARI
  881.     cprintf("\r\n\r\n");
  882. #endif
  883.     gotoxy(1, 4);
  884.  
  885.     cprintf("   User Name:   %s@%s.%s  (%s)\r\n\r\n", my_stuff.user, my_stuff.my_site,
  886.             my_stuff.my_domain, my_stuff.my_name);
  887.  
  888.     cprintf("   Local node name:          %s\r\n", my_stuff.my_site);
  889.     cprintf("   Mail Server:              %s\r\n", my_stuff.mail_server);
  890.     cprintf("   Organization:             %s\r\n\r\n", my_stuff.my_organisation);
  891.  
  892.     cprintf("   Signature File:           %s%s\r\n", my_stuff.home, my_stuff.signature);
  893.     cprintf("   Reply To:                 %s\r\n\r\n", my_stuff.replyuser);
  894.     cprintf("   File Editor:              %s\r\n", my_stuff.editor);
  895.     cprintf("   Edit Parameters:          %s\r\n\r\n", my_stuff.edit_line);
  896.  
  897.     cprintf("   News Home Directory:      %s\r\n", my_stuff.news_dir);
  898.     cprintf("   Mail Directory:           %s\r\n", my_stuff.mail_dir);
  899.     cprintf("   Spool Directory:          %s\r\n\r\n", my_stuff.spooldir);
  900.  
  901.     cprintf("   Posts saved to user:      %s\r\n", my_stuff.mailuser);
  902.     cprintf("   Mail saved to user:       %s\r\n", my_stuff.maillog);
  903.     cprintf("   Extracts saved to user:   %s\r\n\r\n", my_stuff.extruser);
  904.  
  905.     message("-- Press any key for next page --");
  906.     getch();
  907.  
  908.     textbackground(helpb);
  909.     textcolor(helpf);
  910.     clrscr();
  911.     textbackground(headb);
  912.     textcolor(headf);
  913. #ifdef ATARI
  914.     printf("%*s", scr_cols, " ");
  915. #else
  916.     clreol();
  917. #endif
  918.     sprintf(buf, "Pre-set User values  (%s)\r\n", VERSION);
  919.     gotoxy((scr_cols/2) - (strlen(buf)/2), 1);
  920.     cprintf(buf);
  921. #ifndef ATARI
  922.     clreol();
  923. #endif
  924.     textbackground(helpb);
  925.     textcolor(helpf);
  926. #ifndef ATARI
  927.     cprintf("\r\n\r\n");
  928. #endif
  929.     gotoxy(1, 4);
  930.  
  931.     cprintf("   Quoting string:           \"%s\"\r\n", my_stuff.quotemark);
  932.     cprintf("   News articles mailed to:  %s@%s\r\n\r\n", my_stuff.mail_id, my_stuff.mail_server);
  933.  
  934.     cprintf("   Temporary file location:  %s\r\n", my_stuff.temp_name);
  935.     cprintf("   Incoming news directory:  %s\r\n", my_stuff.incoming_dir);
  936.     cprintf("   Mail home directory:      %s\r\n", my_stuff.home);
  937.     cprintf("   ka9q nntp directory:      %s\r\n", my_stuff.nntp_dir);
  938.     cprintf("   Alias name file:          %s\r\n\r\n", my_stuff.alias_file);
  939.  
  940.     cprintf("   Local posting enabled:    %s\r\n", my_stuff.localpost ? "Yes" : "No");
  941.     cprintf("   Mark local post as read:  %s\r\n\r\n", my_stuff.localread ? "Yes" : "No");
  942.     cprintf("   Prompt before exit:       %s\r\n", my_stuff.exitconfirm ? "No" : "Yes");
  943.     cprintf("   Expert mode:              %s\r\n", my_stuff.expert ? "Yes" : "No");
  944.     cprintf("   TAB Style:                %s\r\n", my_stuff.tab_action ? "New" : "Old");
  945.     cprintf("   Quote Headers:            %s\r\n", my_stuff.header_quote ? "Quoted" : "Not Quoted");
  946.     cprintf("   Quote Footers:            %s\r\n", my_stuff.footer_quote ? "Quoted" : "Not Quoted");
  947. //    cprintf("   New Threads Only:         %s\r\n", my_stuff.show_unread ? "No" : "Yes");
  948.     cprintf("   Subject Match Length:     %d characters\r\n", my_stuff.match_len);
  949.  
  950.     message("-- Press any key to continue --");
  951.     getch();
  952.  
  953. }
  954.  
  955. /*------------- Change selected config values for the session -------------*/
  956. void            change_values()
  957. {
  958.  
  959.     int             ch;
  960.     char            buf[256];
  961.  
  962.     textbackground(helpb);
  963.     textcolor(helpf);
  964.     clrscr();
  965.     textbackground(headb);
  966.     textcolor(headf);
  967. #ifdef ATARI
  968.     printf("%*s", scr_cols, " ");
  969. #else
  970.     clreol();
  971. #endif
  972.     sprintf(buf, "Settings for this session (%s)\r\n", VERSION);
  973.     gotoxy((scr_cols/2) - (strlen(buf)/2), 1);
  974.     cprintf(buf);
  975. #ifndef ATARI
  976.     clreol();
  977. #endif
  978.     textbackground(helpb);
  979.     textcolor(helpf);
  980. #ifndef ATARI
  981.     cprintf("\r\n\r\n");
  982. #endif
  983.  
  984.     do
  985.         {
  986.         gotoxy(5, 6);
  987.         cprintf("(E)xpert mode:    %s", my_stuff.expert ? "On " : "Off");
  988.         gotoxy(5, 8);
  989.         cprintf("(T)ab mode:       %s", my_stuff.tab_action ? "New" : "Old");
  990.         gotoxy(5, 10);
  991.         cprintf("(H)eader Quoting: %s", my_stuff.header_quote ? "On " : "Off");
  992.         gotoxy(5, 12);
  993.         cprintf("(F)ooter Quoting: %s", my_stuff.footer_quote ? "On " : "Off");
  994. //        gotoxy(5, 14);
  995. //        cprintf("(S)how Read:      %s", my_stuff.show_unread ? "Off" : "On ");
  996.         message("Enter value to toggle, ESC to exit (E/T/H/F) ");
  997.         textbackground(helpb);
  998.         textcolor(helpf);
  999.         ch = getch();
  1000.         ch = tolower(ch);
  1001.         if (ch == 'q')
  1002.             ch = ESCAPE;
  1003.         switch (ch) {
  1004.             case 'e':
  1005.                 if (my_stuff.expert == TRUE)
  1006.                     my_stuff.expert = FALSE;
  1007.                 else
  1008.                     my_stuff.expert = TRUE;
  1009.                 break;
  1010.             case 't':
  1011.                 if (my_stuff.tab_action == TRUE)
  1012.                     my_stuff.tab_action = FALSE;
  1013.                 else
  1014.                     my_stuff.tab_action = TRUE;
  1015.                 break;
  1016.             case  'h':
  1017.                 if (my_stuff.header_quote == TRUE)
  1018.                     my_stuff.header_quote = FALSE;
  1019.                 else
  1020.                     my_stuff.header_quote = TRUE;
  1021.                 break;
  1022.             case  'f':
  1023.                 if (my_stuff.footer_quote == TRUE)
  1024.                     my_stuff.footer_quote = FALSE;
  1025.                 else
  1026.                     my_stuff.footer_quote = TRUE;
  1027.                 break;
  1028. //            case 's':
  1029. //                if (my_stuff.show_unread == TRUE)
  1030. //                    my_stuff.show_unread = FALSE;
  1031. //                else
  1032. //                    my_stuff.show_unread = TRUE;
  1033. //                break;
  1034.             }
  1035.     }
  1036.     while (ch != ESCAPE);
  1037.     lmessage("");
  1038. }
  1039.  
  1040. /*-------------------- show the list of active groups -----------------------*/
  1041. void            show_groups(ACTIVE ** top, ACTIVE * this_group, int force, ACTIVE *head)
  1042. {
  1043.  
  1044.     /*
  1045.      * This routine takes 'top', a pointer to the first line on the screen
  1046.      * and 'this_group' a pointer to where we want to be, and updates the
  1047.      * screen. A marker to this_group is maintained, and the screen is
  1048.      * repainted, where necessary
  1049.      */
  1050.  
  1051.     static          last_y;
  1052.     static          last_index;
  1053.     int             i, ur;
  1054.     ACTIVE         *that;
  1055.     char            buf[80], lbrack, rbrack;
  1056.  
  1057.     /*
  1058.      * If 'this_group' is above the 'top' or it is more than a screen
  1059.      * length below, or 'this_group and 'top' are both zero, ie first
  1060.      * time repaint the screen
  1061.      */
  1062.  
  1063.     if (force ||
  1064.         ((*top)->index > this_group->index) ||
  1065.         (this_group->index - (*top)->index) > PAGE_LENGTH - 1) {
  1066.  
  1067.         clrscr();
  1068.         textbackground(headb);
  1069.         textcolor(headf);
  1070. #ifdef ATARI
  1071.         for (i=0; i < 2; i++)
  1072.             printf("%*s", scr_cols, " ");
  1073. #else
  1074.         clreol();
  1075. #endif
  1076.         gotoxy((scr_cols/2) - 8, 1);
  1077.         cprintf("Select Newsgroup\r\n");
  1078. #ifndef ATARI
  1079.         clreol();
  1080. #endif
  1081.         gotoxy(scr_cols - 20, 2);
  1082.         cprintf("Group %3d of %3d\r\n", last_index+1, head->groups);
  1083. #ifndef ATARI
  1084.         clreol();
  1085. #endif
  1086.         textbackground(textb);
  1087.         textcolor(textf);
  1088.  
  1089.         /* now adjust the top */
  1090.         *top = this_group;
  1091.         for (i = 0; i < PAGE_LENGTH / 2; i++) {
  1092.             if ((*top)->last == NULL)
  1093.                 break;
  1094.             *top = (*top)->last;
  1095.         }
  1096.  
  1097.         that = *top;
  1098.         for (i = 0; i < PAGE_LENGTH; i++) {
  1099.             ur = count_unread_in_group(that);
  1100.             gotoxy(8, i+5);
  1101.             if (that->suspend == TRUE) {
  1102.                 lbrack = '[';
  1103.                 rbrack = ']';
  1104.             }
  1105.             else
  1106.                 lbrack = rbrack = ' ';
  1107.             if (ur > 0) {
  1108.                 printf("%4d %4ld %c%s%c ", ur, that->hi_num - that->lo_num,
  1109.                     lbrack, that->group, rbrack);
  1110.             }
  1111.             else
  1112.                 printf("     %4ld %c%s%c", that->hi_num - that->lo_num,
  1113.                         lbrack, that->group, rbrack);
  1114.             that = that->next;
  1115.             if (that == NULL)
  1116.                 break;
  1117.         }
  1118.  
  1119.         last_y = this_group->index - (*top)->index;
  1120.         gotoxy(4, last_y + PAGE_HEADER+1);
  1121.         putch('-'); putch('>');
  1122.         last_index = this_group->index;
  1123.  
  1124.     }
  1125.     else {                                                              
  1126.  
  1127.         gotoxy(4, last_y + PAGE_HEADER+1);
  1128.         putch(' '); putch(' ');               /* deliberate putch for HBJ */
  1129.  
  1130.         last_y += (this_group->index - last_index);
  1131.         gotoxy(4, last_y + PAGE_HEADER+1);
  1132.         putch('-'); putch('>');
  1133.         last_index = this_group->index;
  1134.  
  1135.     }
  1136.     gotoxy(scr_cols - 20,2);
  1137.     textbackground(headb); textcolor(headf);
  1138.     cprintf("Group %3d of %3d\r\n", last_index+1, head->groups);
  1139.     textbackground(textb); textcolor(textf);
  1140.  
  1141.     sprintf(buf, "ESC=quit   TAB=next unread group   ENTER=read group   F1 or 'h'=help [%ldk]",
  1142.         farcoreleft() / 1024);
  1143.     command(buf);
  1144. }
  1145.  
  1146.  
  1147.  
  1148.  
  1149. /*--------------------------- process message -------------------------------*/
  1150. int             read_group(ACTIVE * gp)
  1151. {
  1152.  
  1153.     /*
  1154.      * We now have newsgroup.    Access the directory and try to read
  1155.      * the newsgroup articles, extracting the headers.
  1156.      */
  1157.  
  1158.     ARTICLE        *start;
  1159.     int             exit_code;
  1160. #ifdef ATARI
  1161. //    int i;
  1162. #endif
  1163.  
  1164.     if (gp->lo_num < gp->hi_num) {
  1165.  
  1166.         clrscr();
  1167.          textbackground(headb);
  1168.          textcolor(headf);
  1169. #ifdef ATARI
  1170. //         for (i=0; i < 3; i++)
  1171. //             printf("%*s", scr_cols, " ");
  1172. #else
  1173.         clreol();
  1174.         gotoxy((scr_cols/2) - 7, 1);
  1175.          printf("Select Thread\n");
  1176.          clreol();
  1177.          printf("Group: %s ", gp->group);
  1178.          clreol();
  1179. #endif
  1180.          textbackground(textb);
  1181.          textcolor(textf);
  1182.  
  1183.         start = get_headers(gp);
  1184.  
  1185.         exit_code = select_thread(gp, start);
  1186.  
  1187.         free_header(start);
  1188.         start = NULL;
  1189.     }
  1190.     return (exit_code);
  1191. }
  1192.  
  1193.  
  1194. /*------------------------- show the list of threads -----------------------*/
  1195. void            show_threads(ACTIVE * gp, ARTICLE ** top, ARTICLE * this_group,
  1196.             int force, ARTICLE *head)
  1197. {
  1198.  
  1199.     /*
  1200.      * This routine takes 'top', a pointer to the first line on the screen
  1201.      * and 'this_group' a pointer to where we want to be, and updates the
  1202.      * screen. A maker to this_group is maintained, and the screen is
  1203.      * repainted, where necessary
  1204.      */
  1205.  
  1206.     static          last_y;
  1207.     static          last_index;
  1208.     int             i;
  1209.     ARTICLE        *that;
  1210.     int             unread;
  1211.     int             ur;
  1212.     char            buf[80];
  1213.  
  1214.     /*
  1215.      * If 'this_group' is above the 'top' or it is more than a screen
  1216.      * length below, or'this_group and 'top' are both zero, ie first
  1217.      * time repaint the screen
  1218.      */
  1219.  
  1220.     if (force ||
  1221.         ((*top)->index > this_group->index) ||
  1222.         (this_group->index - (*top)->index) > PAGE_LENGTH - 1) {
  1223.  
  1224.         for(that=head, ur=0;that;that=that->next)
  1225.             ur += count_unread_in_thread(gp, that);
  1226.  
  1227.         clrscr();
  1228.         textbackground(headb);
  1229.         textcolor(headf);
  1230. #ifdef ATARI
  1231.         for (i=0; i < 3; i++)
  1232.             printf("%*s", scr_cols, " ");
  1233. #else
  1234.         clreol();
  1235. #endif
  1236.         gotoxy((scr_cols/2) - 7, 1);
  1237.         cprintf("Select Thread\r\n");
  1238. #ifndef ATARI
  1239.         clreol();
  1240. #endif                
  1241.         cprintf("Group: %s", gp->group);
  1242.         gotoxy(scr_cols - 25, 2);
  1243.         cprintf("%4ld Articles, %4d unread\r\n", gp->hi_num - gp->lo_num, ur);
  1244. #ifndef ATARI
  1245.         clreol();
  1246. #endif
  1247.         gotoxy(scr_cols - 25, 3);
  1248.         cprintf("Thread %4d of %4d", last_index+1, gp->threads);
  1249.         textbackground(textb);
  1250.         textcolor(textf);
  1251.         
  1252.         /* now adjust the top */
  1253.         *top = this_group;
  1254.         for (i = 0; i < PAGE_LENGTH / 2; i++) {
  1255.             if ((*top)->last == NULL)
  1256.                 break;
  1257.             *top = (*top)->last;
  1258.         }
  1259.  
  1260.         that = *top;
  1261.         for (i = 0; i < PAGE_LENGTH; i++) {
  1262.             unread = count_unread_in_thread(gp, that);
  1263.             gotoxy(8, i + 5);
  1264.             if (unread > 0)
  1265.                 printf("%4d %4d %s", unread, that->num_articles, that->header);
  1266.             else
  1267.                 printf("     %4d %s", that->num_articles, that->header);
  1268.             that = that->next;
  1269.             if (that == NULL)
  1270.                 break;
  1271.         }
  1272.  
  1273.         last_y = this_group->index - (*top)->index;
  1274.         gotoxy(4, last_y + PAGE_HEADER+1);
  1275.         putch('-'); putch('>');
  1276.         last_index = this_group->index;
  1277.     }
  1278.     else {
  1279.  
  1280.         gotoxy(4, last_y + PAGE_HEADER+1);
  1281.         putch(' '); putch(' ');                 /* deliberate for HBJ */
  1282.         last_y += (this_group->index - last_index);
  1283.         gotoxy(4, last_y + PAGE_HEADER+1);
  1284.         putch('-'); putch('>');
  1285.         last_index = this_group->index;
  1286.  
  1287.     }
  1288.     gotoxy(scr_cols - 25, 3);
  1289.     textbackground(headb); textcolor(headf);
  1290.     cprintf("Thread %4d of %4d", last_index+1, gp->threads);
  1291.     textbackground(textb); textcolor(textf);
  1292.  
  1293.     sprintf(buf, "ESC=select group  TAB=next unread  ENTER=next article  F1 or 'h'=help [%ldk]",
  1294.         farcoreleft() / 1024);
  1295.     command(buf);
  1296. }
  1297.  
  1298. /*-------------------------- search through threads ------------------------*/
  1299. ARTICLE        *search_thread(ACTIVE *gp, ARTICLE *head, int search_body, int direction)
  1300. {
  1301.     int            found = 0, irq = FALSE;
  1302.     ARTICLE       *this_art;
  1303.     ART_ID        *art;
  1304.     TEXT          *tx;
  1305.     LINES         *text;
  1306.     char          *fn;
  1307.     char           prompt[128], pattern[128];
  1308.  
  1309.     if (head == NULL)
  1310.         return head;
  1311.  
  1312.     sprintf(prompt, "Search %s %s for ? [%s] ",
  1313.         search_body ? "articles" : "subjects",
  1314.         direction ? "forwards" : "backwards", search_text);
  1315.     lmessage(prompt);
  1316.     gets(pattern);
  1317.     if (pattern[0] == 0x1b)
  1318.         return 0;
  1319.  
  1320.     if (strlen(pattern) > 0)
  1321.         strcpy(search_text, pattern);
  1322.     else
  1323.         strcpy(pattern, search_text);
  1324.  
  1325.     strlwr(pattern);
  1326.     if (search_body) {
  1327.         message("** searching - please wait (press <ESC> to abort) ***");
  1328.  
  1329.         if (direction) {
  1330.             for (this_art=head; this_art!=NULL; this_art=this_art->next) {
  1331.  
  1332.                 /* for each article */
  1333.                 for (art=this_art->art_num; art!=NULL; art=art->next_art) {
  1334.                     fn = make_news_group_name(gp->group);
  1335.                     tx = load_article(fn, art->art_off, FALSE);
  1336.  
  1337.                     for (text=tx->start; !found && text!=NULL; text=text->next) {
  1338.                         strlwr(text->data);
  1339.                         found = (strstr(text->data, pattern) != NULL);
  1340.                     }
  1341.  
  1342.                     free_article(tx);
  1343.  
  1344.                     if (kbhit())
  1345.                         irq = (getch() == 27);
  1346.  
  1347.                     if (found || irq)
  1348.                         break;
  1349.                 }
  1350.  
  1351.                 if (found || irq)
  1352.                     break;
  1353.             }
  1354.         }
  1355.         else {
  1356.             for (this_art=head->last; this_art!=NULL; this_art=this_art->last) {
  1357.  
  1358.                 /* for each article */
  1359.                 for (art=this_art->art_num; art!=NULL; art=art->next_art) {
  1360.                     fn = make_news_group_name(gp->group);
  1361.                     tx = load_article(fn, art->art_off, FALSE);
  1362.  
  1363.                     for (text=tx->start; !found && text!=NULL; text=text->next) {
  1364.                         strlwr(text->data);
  1365.                         found = (strstr(text->data, pattern) != NULL);
  1366.                     }
  1367.  
  1368.                     free_article(tx);
  1369.  
  1370.                     if (kbhit())
  1371.                         irq = (getch() == 27);
  1372.  
  1373.                     if (found || irq)
  1374.                         break;
  1375.                 }
  1376.  
  1377.                 if (found || irq)
  1378.                     break;
  1379.             }
  1380.         }
  1381.     }
  1382.     else {
  1383.  
  1384.         if (direction) {
  1385.             for (this_art=head; this_art!=NULL; this_art=this_art->next) {
  1386.                 strcpy(prompt, this_art->header);
  1387.                 strlwr(prompt);
  1388.                 found = (strstr(prompt, pattern) != NULL);
  1389.  
  1390.                 if (found)
  1391.                     break;
  1392.             }
  1393.         }
  1394.         else {
  1395.             for (this_art=head->last; this_art!=NULL; this_art=this_art->last) {
  1396.                 strcpy(prompt, this_art->header);
  1397.                 strlwr(prompt);
  1398.                 found = (strstr(prompt, pattern) != NULL);
  1399.  
  1400.                 if (found)
  1401.                     break;
  1402.             }
  1403.         }
  1404.     }
  1405.     if (!found && !irq) {
  1406.         sprintf(prompt, "*** '%s' not found - press any key ***", pattern);
  1407.         message(prompt);
  1408.         getch();
  1409.     }
  1410.     return found ? this_art : head;
  1411. }
  1412.